package de.le.mfw.server
import scala.util.Random

import org.mindrot.jbcrypt.BCrypt

import com.google.gwt.user.client.rpc.SerializationException

import de.le.mfw.server.model.User

import com.google.gwt.user.client.rpc.SerializationException

import javax.servlet.http.HttpServletRequest

import javax.servlet.http.HttpServletResponse

import com.google.gwt.user.client.rpc.RemoteService

import com.google.appengine.api.users.UserServiceFactory

import de.le.mfw.shared.UserDTO;

import de.le.mfw.client.main.service.UserService
import com.google.gwt.user.server.rpc.RemoteServiceServlet

class UserServiceImpl extends RemoteServiceServlet with UserService with Persistence with MailService {

  def register(nick: String, email: String, password: String) = {
    if (userExist(nick)) throw new SerializationException("Nick schon vorhanden")
    else {
      val generatedActivisionToken = Random.nextLong.toHexString
      val user = new User(nick, createPasswordHash(password), email, generatedActivisionToken)
      persist(user)

      sendMail(new Mail(email,
        "Aktivierungslink", createContentForActivationEmail(user, generatedActivisionToken)))
    }
  }

  private def userExist(nick: String): Boolean =
    findObjectsByCriteria(classOf[User], new Criteria("nick", nick)) match {
      case Some(_) => true
      case None => false
    }

  private def createContentForActivationEmail(user: User, generatedActivisionToken: String) = {
    "Hallo " + user.nick + ", \n\n" +
      "Um deinen Account freizuschalten, musst du auf folgenden Link klicken: \n" +
      getThreadLocalRequest.getRequestURL + "?id=" + user.id + "&token=" + generatedActivisionToken +
      "\n\n" + "Dein Magic-For-Web Team"
  }

  private def createPasswordHash(password: String): String =
    BCrypt.hashpw(password, BCrypt.gensalt())

  override def doGet(request: HttpServletRequest, response: HttpServletResponse) = processActivationRequest(request, response)

  private def processActivationRequest(request: HttpServletRequest, response: HttpServletResponse) = {
    val out = response.getWriter()
    try {
      val query = request.getQueryString.asInstanceOf[String]
      val (id, token) = extractArguments(query)
      activateUser(id, token)
      out.println(createActivationHtmlPage(request, "Account erfolgreich aktiviert!"))
    } catch {
      case e => out.println(createActivationHtmlPage(request, "Ungültige URL! (" + e.getLocalizedMessage + ")"))
    }
  }

  private def extractArguments(query: String) = {
    val args = query.split("&")
    if (args.size != 2) throw new Exception("falsche Parameteranzahl!")
    else (extractId(args(0)), extractToken(args(1)))
  }

  private def extractId(arg: String) = {
    if (!arg.startsWith("id=")) throw new Exception("Parameter: id nicht gefunden!")
    else arg.substring(3).toLong
  }

  private def extractToken(arg: String) = {
    if (!arg.startsWith("token=")) throw new Exception("Paramter: token nicht gefunden!")
    else arg.substring(6)
  }

  private def activateUser(id: Long, activationToken: String) = {
    findObjectById(id, classOf[User]) match {
      case Some(user) if (user.activationToken == activationToken) => update[User](id, classOf[User], _.activate)
      case Some(user) if (user.isActive) => throw new Exception("Account schon aktiviert!")
      case Some(user) => throw new Exception("Aktivierungs-Token ist fehlerhaft!")
      case None => throw new Exception("Account nicht gefunden!")
    }
  }

  private def createActivationHtmlPage(request: HttpServletRequest, content: String) = {
    val url = request.getRequestURL.toString
    val begin = if (url.startsWith("http://")) 7 else 0
    val end = url.indexOf("/", begin)
    val home = url.substring(0, end)
    createActivationHtml(content, home)
  }

  private def createActivationHtml(content: String, home: String) = {
    <html>
      <head>
        <title>Magic-for-Web: Aktivierungs-Service</title>
      </head>
      <body>
        <div style="text-align:center;">
          <h1>Magic for Web</h1>
          <p>{ content }</p>
          <p><a href={ home }>zur Hauptseite</a></p>
        </div>
      </body>
    </html>
  }

  def login(nick: String, password: String): UserDTO = {
    findObjectByCriteria(classOf[User], new Criteria("nick", nick)) match {
      case Some(user) if (!user.isActive) => throw new SerializationException("Account noch nicht aktiviert")
      case Some(user) if (BCrypt.checkpw(password, user.hashedPassword)) => new UserDTO(user.id, user.nick, user.email, false)
      case _ => throw new SerializationException("Nick oder Passwort falsch")
    }
  }

  def sendNewPassword(nick: String) = {
    findObjectByCriteria(classOf[User], new Criteria("nick", nick)) match {
      case Some(user) => {
        val generatedPassword = Random.nextInt.toHexString
        val id = user.id.asInstanceOf[Long]
        update[User](id, classOf[User], _.hashedPassword = createPasswordHash(generatedPassword))

        sendMail(new Mail(user.email,
          "Neues Passwort", createContentForNewPasswordEmail(user, generatedPassword)))
      }
      case _ => throw new SerializationException("Nick unbekannt")
    }
  }

  private def createContentForNewPasswordEmail(user: User, generatedPassword: String) = {
    "Hallo " + user.nick + ", \n\n" +
      "Dein neues Passwort lautet: \n" + generatedPassword +
      "\n\n" + "Dein Magic-For-Web Team"
  }

  def changeEmail(userDTO: UserDTO, newEmail: String) = {
    findObjectByCriteria(classOf[User], new Criteria("id", userDTO.getId)) match {
      case Some(user) => {
        val id = user.id.asInstanceOf[Long]
        update[User](id, classOf[User], _.email = newEmail)
      }
      case _ => throw new SerializationException("Nick unbekannt")
    }
  }

  def changePassword(userDTO: UserDTO, newPassword: String) = {
    findObjectByCriteria(classOf[User], new Criteria("id", userDTO.getId)) match {
      case Some(user) => {
        val id = user.id.asInstanceOf[Long]
        update[User](id, classOf[User], _.hashedPassword = createPasswordHash(newPassword))
      }
      case _ => throw new SerializationException("Nick unbekannt")
    }
  }

}